package dao

import (
	"context"
	"database/sql"
	"errors"
	"github.com/go-sql-driver/mysql"
	"gorm.io/gorm"
	"time"
)

var (
	ErrUserDuplicate = errors.New("邮箱冲突")
	ErrUserNotFound  = gorm.ErrRecordNotFound
)

type UserDAO interface {
	Insert(ctx context.Context, u User) error
	FindByEmail(ctx context.Context, email string) (User, error)
	UpdateUser(ctx context.Context, u User) error
	SelectById(ctx context.Context, id int64) (User, error)
	SelectOrCreateByPhone(ctx context.Context, phone string) (User, error)
	SelectByPhone(ctx context.Context, phone string) (User, error)
	FindByWechat(ctx context.Context, openid string) (User, error)
}

type GormUserDao struct {
	db *gorm.DB
}

func NewGormUserDAO(db *gorm.DB) UserDAO {
	return &GormUserDao{
		db: db,
	}
}

func (dao *GormUserDao) Insert(ctx context.Context, u User) error {
	now := time.Now().UnixMilli()
	u.Ctime = now
	u.Utime = now

	err := dao.db.WithContext(ctx).Create(&u).Error
	// err.(*mysql.MySQLError)是类型断言，判断err是否是mysql.MySQLError
	// 是的话，就返回类型类型，已经true
	if mysqlErr, ok := err.(*mysql.MySQLError); ok {
		const uniqueConflictsErrNo uint16 = 1062
		if mysqlErr.Number == uniqueConflictsErrNo {
			return ErrUserDuplicate
		}
	}
	return err
}

func (dao *GormUserDao) FindByWechat(ctx context.Context, openid string) (User, error) {
	var u User
	err := dao.db.WithContext(ctx).Where("wechat_open_id = ?", openid).First(&u).Error

	return u, err
}

func (dao *GormUserDao) FindByEmail(ctx context.Context, email string) (User, error) {
	var u User
	err := dao.db.WithContext(ctx).Where("email = ?", email).First(&u).Error

	return u, err
}

func (dao *GormUserDao) UpdateUser(ctx context.Context, u User) error {
	now := time.Now().UnixMilli()
	u.Utime = now
	err := dao.db.WithContext(ctx).Where("email = ?", u.Email).Updates(&u).Error

	return err
}

func (dao *GormUserDao) SelectById(ctx context.Context, id int64) (User, error) {
	var u User
	err := dao.db.WithContext(ctx).Where("id = ?", id).First(&u).Error
	return u, err
}

func (dao *GormUserDao) SelectOrCreateByPhone(ctx context.Context, phone string) (User, error) {
	var u User
	err := dao.db.WithContext(ctx).Where("phone = ?", phone).First(&u).Error
	switch err {
	case ErrUserNotFound:
		// 数据库中没有该数据，需要新插入
		u.Phone = sql.NullString{phone, true}
		err = dao.db.WithContext(ctx).Create(&u).Error
		return u, err
	case nil:
		// 查找到了数据
		return u, nil
	default:
		return User{}, err
	}
}

func (dao *GormUserDao) SelectByPhone(ctx context.Context, phone string) (User, error) {
	var u User
	err := dao.db.WithContext(ctx).Where("phone = ?", phone).First(&u).Error
	if errors.Is(err, ErrUserNotFound) {
		return User{}, err
	}
	return u, nil
}

type User struct {
	Id            int64          `gorm:"primaryKey,autoIncrement"`
	Email         sql.NullString `gorm:"unique"`
	Phone         sql.NullString `gorm:"unique"`
	Password      string
	Name          string
	Birthday      string
	Profile       string
	WechatOpenId  sql.NullString `gorm:"unique"`
	WechatUnionId sql.NullString
	Ctime         int64
	Utime         int64
}
